/**
 * 200917:创建
 * 201013：默认值处理
 * 210910：three.js到r131引起的片元着色器片段变化
 * 20220304:[require] to [import]...jordia
 */
import {ShaderMaterial,Material,ShaderLib,UniformsUtils} from 'three';

/**
 * 双贴图材质
 *
 * 通用属性：MeshPhysicalMaterial的所有属性
 *
 * 特有属性：
 *  targetMap:{value:null},目标贴图（第二贴图）
 *  ratio:{value:0.5},两张贴图的混合比例
 *  strength:{value:2},贴图颜色强度
 *  receiveLight:{value:true},是否有光照效果
 *
 *
 *  伪代码示例：
 *      let g=new BoxBufferGeometry();
 *      let m=new DoubleMapMaterial({
 *             map:tex1,
 *             targetMap:tex,
 *             receiveLight: false,
 *             strength:3,
 *             ratio:0.5
 *         });
 *      let mm=new Mesh(g,m);
 *      mm.scale.multiplyScalar(5);
 *      scene.add(mm);
 *      let t=0;
 *      setInterval(()=>{
 *             t+=0.01;
 *             m.ratio=Utils.pingPong(0,1,t);
 *         },30);
 */
export default class DoubleMapMaterial extends ShaderMaterial{
    constructor(values){
        super();
        this._values=values;
        this.init();
    }

    //------------------------------------------对外属性及方法-----------------------------------------

    /**
     * 属性赋值
     * @param values {object} meshPhysicalMaterial所支持的属性及此材质特有的属性
     */
    set values(values){
        for(let key in values){
            if(this.hasOwnProperty(''+key+'')){
                if(values[''+key+'']!==undefined){
                    this[''+key+'']=values[''+key+''];
                }
            }else{
                console.warn(''+key+' is not a property of DoubleMapMaterial!');
            }
        }
        this.needsUpdate=true;
    }

    /**
     * 获取属性对象
     * @returns {*}
     */
    get values(){
        return this._paras;
    }

    /**
     * 拷贝
     * @param source
     * @returns {DoubleMapMaterial}
     */
    copy(source){
        Material.prototype.copy.call( this, source );
        this.values=source.values;
        return this;
    }

    /**
     * 克隆
     * @returns {DoubleMapMaterial}
     */
    clone() {
        let c=new DoubleMapMaterial();
        c.copy(this);
        return c;
    }

    /**
     * 销毁
     */
    dispose() {
        super.dispose();
        this._values=null;
        this._paras=null;
        this._exclusive=null;
        this.getter=null;
        this.setter=null;
    }

    //------------------------------------------私有属性及方法-------------------------------------------

    init(){
        this.initVariable();
        this.initGetter();
        this.initSetter();
        this.initProperty();
        this.initShaders();
        this.initUniforms();
        this.initialize();
    }
    initVariable(){
        this._paras={};
        this._exclusive={
            targetMap:{value:null},
            ratio:{value:this._values.ratio!==undefined?this._values.ratio:0.5},
            strength:{value:this._values.strength||1},
            receiveLight:{value:this._values.receiveLight!==undefined?this._values.receiveLight:true}
        };
        this.getter={};
        this.setter={};
    }
    initSetter(){
        this.setter.default=(key)=> {
            return ()=> {
                if (this.uniforms.hasOwnProperty(key)) {
                    return this.uniforms[key].value;
                }
                return this._paras[key];
            };
        };
        this.setter.receiveLight=(key)=> {
            return ()=> {
                return this._paras[key];
            };
        };
    }
    initGetter() {
        this.getter.default = (key) => {
            return (v) => {
                if (this.uniforms.hasOwnProperty(key)) {
                    this.uniforms[key].value = v;
                }
                this._paras[key] = v;
            };
        };
        this.getter.targetMap=(key)=>{
            return (v)=>{
                if(v){
                    if(!this.defines['USE_DST']) {
                        if(!this.defines['USE_SRC']){
                            this.defines['USE_UV'] = '';
                            this.defines['USE_MAP'] = '';
                        }
                        this.defines['USE_DST']='';
                    }
                }else{
                    if(this.defines['USE_DST']){
                        if(!this.defines['USE_SRC']){
                            delete this.defines['USE_MAP'];
                            delete this.defines['USE_UV'];
                        }
                        delete this.defines['USE_DST'];
                    }
                }
                this.uniforms[key].value=v;
                if(!this.uniforms['map'].value)this.uniforms['map'].value=v;
                this._paras[key] = v;
            };
        };
        this.getter.map=(key)=>{
            return (v)=>{
                if(v){
                    if(!this.defines['USE_SRC']) {
                        if(!this.defines['USE_DST']){
                            this.defines['USE_UV'] = '';
                            this.defines['USE_MAP'] = '';
                        }
                        this.defines['USE_SRC']='';
                    }
                }else{
                    if(this.defines['USE_SRC']){
                        if(!this.defines['USE_DST']){
                            delete this.defines['USE_MAP'];
                            delete this.defines['USE_UV'];
                        }
                        delete this.defines['USE_SRC'];
                    }
                }
                this.uniforms[key].value=v;
                this._paras[key] = v;
            };
        };
        this.getter.receiveLight=(key)=>{
            return (v)=>{
                if(v){
                    this.defines['RECEIVE_LIGHT']='';
                }else{
                    delete this.defines['RECEIVE_LIGHT'];
                }
                this._paras[key] = v;
            };
        };
    }
    getGetter(key){
        if(this.getter.hasOwnProperty(key)){
            return this.getter[key](key);
        }else{
            return this.getter.default(key);
        }
    }
    getSetter(key){
        if(this.setter.hasOwnProperty(key)){
            return this.setter[key](key);
        }else{
            return this.setter.default(key);
        }
    }
    definePropertyByObject(obj){
        for(let key of Reflect.ownKeys(obj)){
            if(key!=='constructor'&&key!=="prototype"&&key!=="name"&&key!=='id'&&key!=='uuid'){
                let ds=Object.getOwnPropertyDescriptor(obj,key);
                this._paras[key]=ds.value.value;
                let fc={
                    enumerable: true,
                    configurable: true,
                    set:this.getGetter(key),
                    get:this.getSetter(key)
                };
                Object.defineProperty(this,key,fc);
            }
        }
    }
    initProperty(){
        this.definePropertyByObject(ShaderLib.physical.uniforms);
        this.definePropertyByObject(this._exclusive);
        this.type='DoubleMapMaterial';
        this.defines['PHYSICAL']='';
        this.lights=true;
        this.clipping=true;
    }
    initShaders(){
        this.vertexShader=ShaderLib.physical.vertexShader;
        this.fragmentShader=this.getFragmentShader();
    }
    initUniforms(){
        this.uniforms=UniformsUtils.merge( [ShaderLib.physical.uniforms,this._exclusive] );
        this._exclusive=null;
    }
    initialize(){
        this.values=this._values;
        this._values=null;
    }
    getFragmentShader(){
        return [
            "#define STANDARD" ,
            "#ifdef PHYSICAL" ,
            "#define REFLECTIVITY" ,
            "#define CLEARCOAT" ,
            "#define TRANSMISSION" ,
            "#endif" ,
            "uniform vec3 diffuse;" ,
            "uniform vec3 emissive;" ,
            "uniform float roughness;" ,
            "uniform float metalness;" ,
            "uniform float opacity;" ,
            "#ifdef TRANSMISSION" ,
            "uniform float transmission;" ,
            "#endif" ,
            "#ifdef REFLECTIVITY" ,
            "uniform float reflectivity;" ,
            "#endif" ,
            "#ifdef CLEARCOAT" ,
            "uniform float clearcoat;" ,
            "uniform float clearcoatRoughness;" ,
            "#endif" ,
            "#ifdef USE_SHEEN" ,
            "uniform vec3 sheen;" ,
            "#endif" ,
            "varying vec3 vViewPosition;" ,
            "#ifndef FLAT_SHADED" ,
            "varying vec3 vNormal;" ,
            "#ifdef USE_TANGENT" ,
            "varying vec3 vTangent;" ,
            "varying vec3 vBitangent;" ,
            "#endif" ,
            "#endif" ,
            "#include <common>" ,
            "#include <packing>" ,
            "#include <dithering_pars_fragment>" ,
            "#include <color_pars_fragment>" ,
            "#include <uv_pars_fragment>" ,
            "#include <uv2_pars_fragment>" ,
            "#include <map_pars_fragment>" ,
            "#include <alphamap_pars_fragment>" ,
            "#include <aomap_pars_fragment>" ,
            "#include <lightmap_pars_fragment>" ,
            "#include <emissivemap_pars_fragment>" ,
            // "#include <transmissionmap_pars_fragment>" ,210910
            "#include <transmission_pars_fragment>" ,
            "#include <bsdfs>" ,
            "#include <cube_uv_reflection_fragment>" ,
            "#include <envmap_common_pars_fragment>" ,
            "#include <envmap_physical_pars_fragment>" ,
            "#include <fog_pars_fragment>" ,
            "#include <lights_pars_begin>" ,
            "#include <lights_physical_pars_fragment>" ,
            "#include <shadowmap_pars_fragment>" ,
            "#include <bumpmap_pars_fragment>" ,
            "#include <normalmap_pars_fragment>" ,
            "#include <clearcoat_pars_fragment>" ,
            "#include <roughnessmap_pars_fragment>" ,
            "#include <metalnessmap_pars_fragment>" ,
            "#include <logdepthbuf_pars_fragment>" ,
            "#include <clipping_planes_pars_fragment>" ,
            "uniform float strength;" ,
            "uniform float ratio;" ,
            "uniform sampler2D targetMap;" ,
            "#define white vec4(1.)" ,
            "void main() {" ,
            "   #include <clipping_planes_fragment>" ,
            "   vec4 diffuseColor = vec4( diffuse, opacity );" ,
            "   ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );" ,
            "   vec3 totalEmissiveRadiance = emissive;" ,
            "   #ifdef TRANSMISSION" ,
            "       float totalTransmission = transmission;" ,
            "   #endif" ,
            "   #include <logdepthbuf_fragment>" ,

            "   vec4 texelColor=vec4(0.);" ,
            "   #ifdef USE_SRC" ,
            "       texelColor+=texture2D( map, vUv )*(1.-ratio);" ,
            "   #else" ,
            "       texelColor+=white*(1.-ratio);" ,
            "   #endif" ,
            "   #ifdef USE_DST" ,
            "       texelColor+=texture2D( targetMap, vUv )*ratio;" ,
            "   #else" ,
            "       texelColor+=white*ratio;" ,
            "   #endif" ,
            "   #ifdef USE_MAP" ,
            "      texelColor = mapTexelToLinear( texelColor );" ,
            "      diffuseColor *= texelColor;" ,
            "   #endif" ,

            "   #include <color_fragment>" ,
            "   #include <alphamap_fragment>" ,
            "   #include <alphatest_fragment>" ,

            "   #ifdef RECEIVE_LIGHT" ,
            "      #include <roughnessmap_fragment>" ,
            "      #include <metalnessmap_fragment>" ,
            "      #include <normal_fragment_begin>" ,
            "      #include <normal_fragment_maps>" ,
            "      #include <clearcoat_normal_fragment_begin>" ,
            "      #include <clearcoat_normal_fragment_maps>" ,
            "   #endif" ,

            "   #include <emissivemap_fragment>" ,
            // "   #include <transmissionmap_fragment>" ,210910
            "   #include <transmission_fragment>" ,
            "   #ifdef RECEIVE_LIGHT" ,
            "      #include <lights_physical_fragment>" ,
            "      #include <lights_fragment_begin>" ,
            "      #include <lights_fragment_maps>" ,
            "      #include <lights_fragment_end>" ,
            "      #include <aomap_fragment>" ,
            "   #endif" ,

            "   vec3 outgoingLight =vec3(0.);" ,
            "   #ifdef RECEIVE_LIGHT" ,
            "      outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse*strength + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;" ,
            "   #else" ,
            "      outgoingLight =diffuseColor.rgb*strength+totalEmissiveRadiance;" ,
            "   #endif" ,
            "   #ifdef TRANSMISSION" ,
            "       diffuseColor.a *= saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) );" ,
            "   #endif" ,
            "   gl_FragColor = vec4( outgoingLight, diffuseColor.a );" ,
            "   #include <tonemapping_fragment>" ,
            "   #include <encodings_fragment>" ,
            "   #include <fog_fragment>" ,
            "   #include <premultiplied_alpha_fragment>" ,
            "   #include <dithering_fragment>" ,
            "}"
        ].join('\n');
    }
}